﻿Imports Aricie.DNN.Caching.Client
Imports Aricie.DNN.Caching.Analysis
Imports Aricie.DNN.Diagnostics
Imports Aricie.Collections
Imports Aricie.ComponentModel
Imports DotNetNuke.Common.Utilities
Imports System.Reflection
Imports Aricie.Services
Imports Aricie.DNN.Services.Workers
Imports System.Threading

Namespace Aricie.DNN.Caching
    Public Class DistributedCachingProviderBase
     
#Region "Private members"
        'Fields
        Public Shared Instance As New DistributedCachingProviderBase

        Private Const ProviderType As String = "caching"

        Private Shared _WebCache As System.Web.Caching.Cache

        Private Shared _Settings As DistributedCachingProviderSettings

        Private Shared _SettingsLock As New Object

        Private Shared _DistributedCachingController As CachingClientController

        Private _CurrentInserts As New Dictionary(Of String, Object)

        Private Shared _ClusterCatalog As SerializableDictionary(Of String, Boolean)
        Private Shared _UpdateCatalog As Boolean
        Private Shared _RetrievingCatalog As Boolean
        Private Shared _EmptyCatalog As New SerializableDictionary(Of String, Boolean)

        Private WithEvents _AsynchronousDistributedCachingInsertTaskQueue As TaskQueue(Of CachePutJob)
        Private WithEvents _SynchronizerPushTaskQueue As TaskQueue(Of CacheJob)
        Private WithEvents _SynchronizerFailoverPushTaskQueue As TaskQueue(Of CacheJob)

        'Private WithEvents _SynchronizerPullTaskQueue As TaskQueue(Of CacheJob)
        Private WithEvents _AsynchronousInsertTaskQueue As TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))
        Private WithEvents _AsynchronousGlobalInsertTaskQueue As TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))
        Private WithEvents _AsynchronousBatchGetTaskQueue As TaskQueue(Of CacheJob)
        Private _CurrentAsyncBatchJobs As New Dictionary(Of String, Boolean)
        Private Shared WithEvents _AsynchronousGetTaskQueue As TaskQueue(Of String)

        Private ReadOnly Property AsynchronousDistributedCachingInsertTaskQueue() As TaskQueue(Of CachePutJob)
            Get
                If _AsynchronousDistributedCachingInsertTaskQueue Is Nothing Then
                    _AsynchronousDistributedCachingInsertTaskQueue = New TaskQueue(Of CachePutJob)(New Action(Of CachePutJob)(AddressOf Me.AsynchronousDistributedCachingInsert), DistributedCachingProviderBase.Settings.CloudSettings.DistributedCachingInsertTaskQueueInfo)
                    AddHandler _AsynchronousDistributedCachingInsertTaskQueue.ActionPerformed, AddressOf Me.DistributorTaskQueue_ActionPerformed
                End If
                Return _AsynchronousDistributedCachingInsertTaskQueue
            End Get
        End Property

        Private ReadOnly Property SynchronizerPushTaskQueue() As TaskQueue(Of CacheJob)
            Get
                If _SynchronizerPushTaskQueue Is Nothing Then
                    _SynchronizerPushTaskQueue = New TaskQueue(Of CacheJob)(New Action(Of CacheJob)(AddressOf Me.AsynchronousSynchronizerDependency), DistributedCachingProviderBase.Settings.CloudSettings.SynchronizerPushTaskQueueInfo)
                End If
                Return _SynchronizerPushTaskQueue
            End Get
        End Property


        Private ReadOnly Property SynchronizerFailoverPushTaskQueue() As TaskQueue(Of CacheJob)
            Get
                If _SynchronizerFailoverPushTaskQueue Is Nothing Then
                    _SynchronizerFailoverPushTaskQueue = New TaskQueue(Of CacheJob)(New Action(Of CacheJob)(AddressOf Me.AsynchronousSynchronizerFailoverDependency), DistributedCachingProviderBase.Settings.CloudSettings.SynchronizerFailoverTaskQueueInfo)
                End If
                Return _SynchronizerFailoverPushTaskQueue
            End Get
        End Property


        'Private ReadOnly Property SynchronizerPullTaskQueue() As TaskQueue(Of CacheJob)
        '    Get
        '        If _SynchronizerPullTaskQueue Is Nothing Then
        '            _SynchronizerPullTaskQueue = New TaskQueue(Of CacheJob)(New Action(Of CacheJob)(AddressOf Me.SynchronizerDependency), DistributedCachingProviderBase.Settings.CloudSettings.SynchronizerPullTaskQueueInfo)
        '        End If
        '        Return _SynchronizerPullTaskQueue
        '    End Get
        'End Property

        Private ReadOnly Property AsyncLocalInsertTaskQueue() As TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))
            Get
                If _AsynchronousInsertTaskQueue Is Nothing Then
                    _AsynchronousInsertTaskQueue = New TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))(New Action(Of KeyValuePair(Of String, WrappedCacheObject))(AddressOf Me.AsynchronousLocalInsert), DistributedCachingProviderBase.Settings.CloudSettings.LocalInsertTaskQueueInfo)
                End If
                Return _AsynchronousInsertTaskQueue
            End Get
        End Property

        Private ReadOnly Property AsynchronousGlobalInsertTaskQueue() As TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))
            Get
                If _AsynchronousGlobalInsertTaskQueue Is Nothing Then
                    _AsynchronousGlobalInsertTaskQueue = New TaskQueue(Of KeyValuePair(Of String, WrappedCacheObject))(New Action(Of KeyValuePair(Of String, WrappedCacheObject))(AddressOf Me.AsynchronousGlobalInsert), DistributedCachingProviderBase.Settings.CloudSettings.GlobalInsertTaskQueueInfo)
                End If
                Return _AsynchronousGlobalInsertTaskQueue
            End Get
        End Property

        Private Shared ReadOnly Property AsynchronousGetTaskQueue() As TaskQueue(Of String)
            Get
                If _AsynchronousGetTaskQueue Is Nothing Then
                    _AsynchronousGetTaskQueue = New TaskQueue(Of String)(New Action(Of String)(AddressOf AsynchronousGet), DistributedCachingProviderBase.Settings.CloudSettings.GetTaskQueueInfo)
                End If
                Return _AsynchronousGetTaskQueue
            End Get
        End Property

        Private ReadOnly Property AsynchronousBatchGetTaskQueue() As TaskQueue(Of CacheJob)
            Get
                If _AsynchronousBatchGetTaskQueue Is Nothing Then
                    _AsynchronousBatchGetTaskQueue = New TaskQueue(Of CacheJob)(New Action(Of CacheJob)(AddressOf Me.AsynchronousBatchGet), DistributedCachingProviderBase.Settings.CloudSettings.BatchGetTaskQueueInfo)
                End If
                Return _AsynchronousBatchGetTaskQueue
            End Get
        End Property





#End Region

#Region "cTor"


        Public Sub New()
        End Sub

#End Region

#Region "Properties"


        Public Shared ReadOnly Property ClientController() As CachingClientController
            Get
                If _DistributedCachingController Is Nothing Then
                    _DistributedCachingController = New CachingClientController()
                End If
                Return _DistributedCachingController
            End Get
        End Property

        Public Shared ReadOnly Property WebCache() As Cache
            Get
                If (DistributedCachingProviderBase._WebCache Is Nothing) Then
                    DistributedCachingProviderBase._WebCache = HttpRuntime.Cache

                End If
                Return DistributedCachingProviderBase._WebCache
            End Get
        End Property

        Public Shared Sub LoadSettings()
            SyncLock _SettingsLock
                If DistributedCachingProviderBase._Settings Is Nothing Then
                    DistributedCachingProviderBase._Settings = DistributedCachingBusinessController.LoadSettings(False)
                    'DistributedCachingProviderBase._Settings = DistributedCachingProviderSettings.Instance(False, True)
                End If
            End SyncLock
            DistributedCachingProviderBase.AsynchronousGetTaskQueue.EnqueueTasks(DistributedCachingProviderBase._Settings.CloudSettings.StartupKeys)
        End Sub

        Public Shared ReadOnly Property Settings() As DistributedCachingProviderSettings
            Get
                If DistributedCachingProviderBase._Settings Is Nothing Then
                    LoadSettings()
                End If
                Return DistributedCachingProviderBase._Settings

            End Get
        End Property

#End Region

#Region "Overrides"

        Public Function Add(ByVal key As String, ByVal value As Object, ByVal dependencies As System.Web.Caching.CacheDependency, _
                    ByVal absoluteExpiration As Date, ByVal slidingExpiration As System.TimeSpan, ByVal priority As System.Web.Caching.CacheItemPriority, _
                    ByVal onRemoveCallback As System.Web.Caching.CacheItemRemovedCallback) As Object
            Return DistributedCachingProviderBase.WebCache.Add(key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback)

        End Function

        Public Function GetEnumerator() As IDictionaryEnumerator
            Return DistributedCachingProviderBase.WebCache.GetEnumerator
        End Function


        Public Function GetItem(ByVal cacheKey As String) As Object
            Dim toReturn As Object = DistributedCachingProviderBase.WebCache.Get(cacheKey)
            If toReturn Is Nothing Then
                Try
                    If _CurrentInserts.TryGetValue(cacheKey, toReturn) Then
                        If DistributedCachingProviderBase.Settings.LoggingSettings.LogEarlyGets Then
                            Dim strKey As New KeyValuePair(Of String, String)("Cache Key", cacheKey)
                            Dim objDebug As New DebugInfo("DistributedCaching", "Early Local Call", strKey)
                            Aricie.DNN.Diagnostics.SimpleDebugLogger.Instance.AddDebugInfo(objDebug)
                        End If
                        Return toReturn
                    End If

                    Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings

                    Dim objCacheJob As CacheJob = objSettings.CloudSettings.GetKeyJob(cacheKey)

                    If objSettings.EffectiveEnabledDistributedCache Then


                        toReturn = Me.GetFromDistributedCaching(objCacheJob)

                    End If
                    If objCacheJob.ShouldLog Then
                        If toReturn Is Nothing Then
                            DistributedCachingBusinessController.LogCachingOperation(CacheTimings.CreateObject, "Start Create Object", Diagnostics.WorkingPhase.EndOverhead, False, cacheKey, False)
                        End If
                    End If
                Catch ex As Exception
                    ProcessException(ex)
                End Try
            End If
            Return toReturn
        End Function

        Public Function GetPersistentCacheItem(ByVal cacheKey As String, ByVal objType As System.Type) As Object
            Dim objCacheJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(cacheKey)
            If objCacheJob.Strategy.DistributionType = CacheDistributionType.Undefined _
                             AndAlso DotNetNuke.Common.Utilities.DataCache.CachePersistenceEnabled Then
                objCacheJob.Strategy.DistributionType = CacheDistributionType.XmlSerialized
            End If
            Return GetItem(cacheKey)
        End Function

        Public Overloads Sub Insert(ByVal cacheKey As String, ByVal objObject As Object, ByVal persistAppRestart As Boolean)

            Me.Insert(cacheKey, objObject, Nothing, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Normal, Nothing, persistAppRestart)
        End Sub

        Public Overloads Sub Insert(ByVal cacheKey As String, ByVal objObject As Object, ByVal objDependency As System.Web.Caching.CacheDependency, ByVal persistAppRestart As Boolean)

            Me.Insert(cacheKey, objObject, objDependency, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Normal, Nothing, persistAppRestart)

        End Sub

        Public Overloads Sub Insert(ByVal cacheKey As String, ByVal objObject As Object, ByVal objDependency As System.Web.Caching.CacheDependency, ByVal absoluteExpiration As Date, ByVal slidingExpiration As System.TimeSpan, ByVal persistAppRestart As Boolean)
            Me.Insert(cacheKey, objObject, objDependency, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, Nothing, persistAppRestart)
        End Sub


        Public Overloads Sub Insert(ByVal cacheKey As String, ByVal objObject As Object, ByVal objDependency As System.Web.Caching.CacheDependency, _
                                              ByVal absoluteExpiration As Date, ByVal slidingExpiration As System.TimeSpan, _
                                              ByVal priority As System.Web.Caching.CacheItemPriority, _
                                              ByVal onRemoveCallback As System.Web.Caching.CacheItemRemovedCallback, ByVal persistAppRestart As Boolean)



            If Not String.IsNullOrEmpty(cacheKey) AndAlso objObject IsNot Nothing Then
                If DistributedCachingProviderBase.Settings.Enabled Then
                    Try
                        Dim objCacheJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(cacheKey)

                        SyncLock _CurrentInserts
                            _CurrentInserts(cacheKey) = objObject
                        End SyncLock
                        Dim objWrapped As New WrappedCacheObject(objObject, persistAppRestart, objDependency, absoluteExpiration, slidingExpiration, priority, onRemoveCallback)

                        AsynchronousGlobalInsertTaskQueue.EnqueueTask(New KeyValuePair(Of String, WrappedCacheObject)(cacheKey, objWrapped))

                        If objCacheJob.ShouldLog Then
                            DistributedCachingBusinessController.LogCachingOperation(CacheTimings.CreateObject, "End Create Object", _
                                                                           Diagnostics.WorkingPhase.InProgress, True, cacheKey, True)
                        End If
                    Catch ex As Exception

                        ProcessException(ex)
                        If _CurrentInserts.ContainsKey(cacheKey) Then
                            SyncLock _CurrentInserts
                                _CurrentInserts.Remove(cacheKey)
                            End SyncLock
                        End If
                    End Try
                Else
                    DistributedCachingProviderBase.WebCache.Insert(cacheKey, objObject, objDependency, absoluteExpiration, slidingExpiration, priority, onRemoveCallback)
                End If
            Else
                Dim strObj As String = "Null"
                If objObject IsNot Nothing Then
                    strObj = objObject.ToString
                End If
                Dim strKey As String = "Null"
                If cacheKey IsNot Nothing Then
                    strKey = cacheKey
                End If
                Throw New ArgumentException(String.Format("key and object can't be null for local cache insert, key:{0}, object:{1}", strKey, strObj))
            End If




        End Sub


        Public Function PurgeCache() As String
            Return "The DistributedCaching Caching Provider does not require the PurgeCache feature"
        End Function

        Public Sub Remove(ByVal cacheKey As String)
            Try
                If _CurrentInserts.ContainsKey(cacheKey) Then
                    SyncLock _CurrentInserts
                        _CurrentInserts.Remove(cacheKey)
                    End SyncLock
                End If
                If (DistributedCachingProviderBase.WebCache.Item(cacheKey) IsNot Nothing) Then
                    DistributedCachingProviderBase.WebCache.Remove(cacheKey)
                    If DistributedCachingProviderBase.Settings.EffectiveDistributedCachingEnabled Then
                        Dim objCacheJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(cacheKey)
                        RemoveFromDistributedCaching(objCacheJob)
                    End If
                End If
            Catch ex As Exception
                ProcessException(ex)
            End Try
        End Sub


        Public Sub RemovePersistentCacheItem(ByVal cacheKey As String)
            Me.Remove(cacheKey)
        End Sub


#End Region

#Region "Public methods"

        Public Shared Function GetAllLocalKeys() As List(Of String)
            Dim enumTor As IDictionaryEnumerator = DistributedCachingProviderBase.WebCache.GetEnumerator
            Dim toReturn As New List(Of String)
            While enumTor.MoveNext
                toReturn.Add(enumTor.Key.ToString)
            End While
            Return toReturn
        End Function

        Public Shared Sub ClearCache(ByVal useGlobalDependency As Boolean)
            Try
                If useGlobalDependency Then
                    DistributedCachingProviderBase.Instance.Remove(DistributedCachingProviderBase.Settings.CloudSettings.GlobalDependencyKey)
                Else
                    Dim keys As List(Of String) = GetAllLocalKeys()
                    For Each key As String In GetAllLocalKeys()
                        DistributedCachingProviderBase.WebCache.Remove(key)
                    Next
                End If
            Catch ex As Exception
                ProcessException(ex)
            End Try


        End Sub

        Public Shared Sub ClearSettings()
            Try

                DistributedCachingProviderBase._Settings = Nothing
                If _DistributedCachingController IsNot Nothing Then
                    _DistributedCachingController.Stop()
                End If
                _DistributedCachingController = Nothing
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub ResetRegions()
            CreateRegions()
            ClearRegions()
        End Sub

        Public Shared Sub CreateRegions()
            Try

                For Each job As CacheJob In DistributedCachingProviderBase.Settings.CloudSettings.GetDefaultJobs
                    ClientController.CreateRegion(job)
                Next
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub ClearRegions()
            Try
                For Each job As CacheJob In DistributedCachingProviderBase.Settings.CloudSettings.GetDefaultJobs
                    ClientController.ClearRegion(job)
                Next
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub DeleteRegions()
            Try
                For Each job As CacheJob In DistributedCachingProviderBase.Settings.CloudSettings.GetDefaultJobs
                    ClientController.DeleteRegion(job)
                Next
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub ResetRegion(ByVal regionName As String)


            CreateRegion(regionName)
            ClearRegion(regionName)


        End Sub

        Public Shared Sub CreateRegion(ByVal regionName As String)

            Try
                Dim objInfo As New CacheStrategy
                objInfo.RegionName = regionName
                objInfo.EvictionsOn = True
                Dim job As New CacheJob(StrategyType.Default, objInfo)
                ClientController.CreateRegion(job)

            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub ClearRegion(ByVal regionName As String)

            Try
                Dim objInfo As New CacheStrategy
                objInfo.RegionName = regionName
                Dim job As New CacheJob(StrategyType.Default, objInfo)
                ClientController.ClearRegion(job)

            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub DeleteRegion(ByVal regionName As String)

            Try
                Dim objInfo As New CacheStrategy
                objInfo.RegionName = regionName
                Dim job As New CacheJob(StrategyType.Default, objInfo)
                ClientController.DeleteRegion(job)
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Shared Sub PutInDistributedCaching(ByVal key As String, ByVal value As Object)
            'Try
            Dim objCacheJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(key)
            Dim wrappedObj As New WrappedCacheObject(value, False)
            DirectCast(DistributedCachingProviderBase.Instance, DistributedCachingProviderBase).PutInDistributedCaching(objCacheJob, wrappedObj)
            'Catch ex As Exception
            '    ProcessException(ex)
            'End Try
        End Sub



#End Region

#Region "Event Handlers methods"



        Public Sub OnCacheItemRemovedCallback(ByVal key As String, _
                    ByVal value As Object, _
                    ByVal reason As CacheItemRemovedReason)
            Try
                If Settings.EffectiveDistributedCachingEnabled Then
                    Select Case reason
                        Case CacheItemRemovedReason.DependencyChanged ', CacheItemRemovedReason.Removed
                            Dim objCacheJob As CacheJob = Settings.CloudSettings.GetKeyJob(key)
                            Me.RemoveFromDistributedCaching(objCacheJob)
                    End Select
                End If
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

        Public Sub OnDataCacheDependencyNotificationCallBack(ByVal key As String, ByVal value As Object, ByVal reason As CacheItemRemovedReason)
            Try
                If Settings.EffectiveDistributedCachingEnabled Then

                    Dim objCacheJob As CacheJob = Settings.CloudSettings.GetJobFromDependency(key)
                    DistributedCachingProviderBase.WebCache.Remove(objCacheJob.Key)

                End If
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub

#End Region

#Region "Private Methods"


        Private Sub AsynchronousGlobalInsert(ByVal objWrappedPair As KeyValuePair(Of String, WrappedCacheObject))
            Try
                Me.GlobalInsert(objWrappedPair, True)
            Catch ex As Exception
                ProcessException(ex)
            End Try
        End Sub

        Private Sub GlobalInsert(ByVal objWrappedPair As KeyValuePair(Of String, WrappedCacheObject), ByVal asyncLocal As Boolean)
            Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings
            Dim objCacheJob As CacheJob = objSettings.CloudSettings.GetKeyJob(objWrappedPair.Key)

            If objWrappedPair.Value.PersistAppRestart AndAlso DataCache.CachePersistenceEnabled Then
                'remove the cache key which
                'will remove the serialized
                'file before creating a new one
                Remove(objWrappedPair.Key)
            End If
            If objSettings.LocalCacheSettings.LocalEnabled Then
                If asyncLocal Then
                    Me.AsyncLocalInsertTaskQueue.EnqueueTask(objWrappedPair)
                Else
                    Me.AsynchronousLocalInsert(objWrappedPair)
                End If


            End If
            If objSettings.EffectiveDistributedCachingEnabled Then

                Me.PutInDistributedCaching(objCacheJob, objWrappedPair.Value)

            End If

        End Sub






        Private Sub AsynchronousLocalInsert(ByVal objPair As KeyValuePair(Of String, WrappedCacheObject))
            Try
                'If Me._CurrentInserts.ContainsKey(objPair.Key) Then
                Dim supKeysJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(objPair.Key)

                LocalInsert(objPair, supKeysJob)





                'End If
            Catch ex As Exception
                ProcessException(ex)
            Finally
                If Me._CurrentInserts.ContainsKey(objPair.Key) Then
                    SyncLock Me._CurrentInserts
                        Me._CurrentInserts.Remove(objPair.Key)
                    End SyncLock
                End If
            End Try

        End Sub


        Private Sub LocalInsert(ByVal objPair As KeyValuePair(Of String, WrappedCacheObject), ByVal objCacheJob As CacheJob)

            If objCacheJob.ShouldLog Then
                DistributedCachingBusinessController.LogCachingOperation(CacheTimings.LocalInsert, "Start Insert Local", _
                                                             Diagnostics.WorkingPhase.InProgress, False, objPair.Key, False)
            End If

            Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings

            Dim objWrappedCache As WrappedCacheObject
            If TypeOf objPair.Value Is WrappedCacheObject Then
                objWrappedCache = DirectCast(objPair.Value, WrappedCacheObject)
            Else
                objWrappedCache = New WrappedCacheObject(objPair.Value, objCacheJob)
            End If

            If objWrappedCache.Value IsNot Nothing Then

                If objSettings.LocalCacheSettings.LocalEnabled Then
                    Me.LocalInsert(objCacheJob, objWrappedCache.Value, objWrappedCache.Dependency, objWrappedCache.AbsoluteExpiration, _
                                   objWrappedCache.SlidingExpriration, objWrappedCache.Priority, objWrappedCache.ItemRemovedCallBack)
                End If

            End If

            If objCacheJob.ShouldLog Then
                If TypeOf objWrappedCache Is WrappedDistributedCacheObject Then
                    Dim distributionTypePair As New KeyValuePair(Of String, String)(CacheStepProperty.DistributionType, _
                                                                                    DirectCast(objWrappedCache, WrappedDistributedCacheObject).DistributionType.ToString)
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.LocalInsert, _
                                    "End Insert Local", Diagnostics.WorkingPhase.InProgress, True, objPair.Key, True, distributionTypePair)
                Else
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.LocalInsert, _
                                    "End Insert Local", Diagnostics.WorkingPhase.InProgress, True, objPair.Key, True)
                End If

            End If
        End Sub

        Private Sub LocalInsert(ByVal objCacheJob As CacheJob, ByVal objObject As Object, ByVal objDependency As System.Web.Caching.CacheDependency, _
                                                   ByVal absoluteExpiration As Date, ByVal slidingExpiration As System.TimeSpan, _
                                                   ByVal priority As System.Web.Caching.CacheItemPriority, _
                                                   ByVal onRemoveCallback As System.Web.Caching.CacheItemRemovedCallback)

            If objCacheJob.ShouldSynchronize OrElse objCacheJob.Strategy.DistributionType <> CacheDistributionType.NotDistributed Then
                Dim depRemoveCallBack As New CacheItemRemovedCallback(AddressOf Me.OnCacheItemRemovedCallback)
                If onRemoveCallback IsNot Nothing Then
                    Dim newRemoveCallBack As CacheItemRemovedCallback = DirectCast(ReflectionHelper.CombineDelegate(depRemoveCallBack, onRemoveCallback), CacheItemRemovedCallback)
                    onRemoveCallback = newRemoveCallBack
                Else
                    onRemoveCallback = depRemoveCallBack
                End If
            End If
            Dim newDep As CacheDependency = objDependency
            If objCacheJob.ShouldAddGlobalDep Then
                Dim objCloudSettings As CloudSettings = DistributedCachingProviderBase.Settings.CloudSettings
                If Me.GetItem(objCloudSettings.GlobalDependencyKey) Is Nothing Then

                    Dim depObject As Boolean = True
                    SyncLock _CurrentInserts
                        _CurrentInserts(objCloudSettings.GlobalDependencyKey) = depObject
                    End SyncLock
                    Dim objGlobalDepJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GlobalDependencyJob
                    Dim objWrapped As New WrappedCacheObject(depObject, objGlobalDepJob)
                    Me.GlobalInsert(New KeyValuePair(Of String, WrappedCacheObject)(objCloudSettings.GlobalDependencyKey, objWrapped), False)
                End If
                newDep = New CacheDependency(Nothing, New String(0) {objCloudSettings.GlobalDependencyKey}, objDependency)
            End If
            If objCacheJob.ShouldLog Then
                DistributedCachingBusinessController.LogCachingOperation(CacheTimings.LocalInsert, "Start Inserting Local", Diagnostics.WorkingPhase.EndOverhead, False, objCacheJob.Key, False)
            End If
            DistributedCachingProviderBase.WebCache.Insert(objCacheJob.Key, objObject, newDep, absoluteExpiration, slidingExpiration, priority, onRemoveCallback)
            If objCacheJob.ShouldSynchronize Then
                SynchronizerPushTaskQueue.EnqueueTask(objCacheJob)
            End If
            If objCacheJob.ShouldLog Then
                DistributedCachingBusinessController.LogCachingOperation(CacheTimings.LocalInsert, "End Inserting Local", Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Key, True)
                Dim autoCOnfig As AutoConfigSettings = DistributedCachingProviderBase.Settings.AutoConfigSettings
                If autoCOnfig.EnablePeriodicAnalysis AndAlso objCacheJob.Key = autoCOnfig.TriggerKey Then
                    DistributedCachingBusinessController.AnalyzeTaskQueue.EnqueueTask(Now)
                End If
            End If
        End Sub


        Private Sub AsynchronousSynchronizerDependency(ByVal objCacheJob As CacheJob)
            Dim synchroOk As Boolean
            Try
                synchroOk = AddSynchronizerDependency(objCacheJob)
            Catch ex As Exception
                synchroOk = False
                ProcessException(ex)
            End Try
            If Not synchroOk Then
                SynchronizerFailoverPushTaskQueue.EnqueueTask(objCacheJob)
            End If

        End Sub

        Private Sub AsynchronousSynchronizerFailoverDependency(ByVal objCacheJob As CacheJob)
            Dim synchroOk As Boolean
            Try
                synchroOk = AddSynchronizerDependency(objCacheJob)
            Catch
                synchroOk = False
            End Try
            If Not synchroOk Then
                SynchronizerFailoverPushTaskQueue.EnqueueTask(objCacheJob)
            End If
        End Sub



        Private Function AddSynchronizerDependency(ByVal objCacheJob As CacheJob) As Boolean
            Dim toReturn As Boolean
            Dim depJob As CacheJob = Settings.CloudSettings.GetDependencyJob(objCacheJob)
            If depJob.Strategy.DistributionType <> CacheDistributionType.NotDistributed AndAlso DistributedCachingProviderBase.ClientController.Get(depJob) Is Nothing Then
                toReturn = DistributedCachingProviderBase.ClientController.Put(Settings.CloudSettings.GetDependencyPutJob(objCacheJob), AddressOf OnDataCacheDependencyNotificationCallBack)
            Else
                toReturn = DistributedCachingProviderBase.ClientController.AddCallBack(depJob, AddressOf OnDataCacheDependencyNotificationCallBack)
            End If
            Return toReturn

        End Function




        ''' <summary>
        ''' Get an object from the distributed caching engine
        ''' </summary>
        ''' <param name="objCacheJob"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function GetFromDistributedCaching(ByRef objCacheJob As CacheJob) As Object

            Dim toReturn As Object = Nothing


            'Try
            Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings
            If objCacheJob.Strategy.DistributionType <> CacheDistributionType.NotDistributed _
                    AndAlso (objSettings.CloudSettings.NoCatalog _
                             OrElse objCacheJob.Strategy.SkipCatalogCheck _
                             OrElse DistributedCachingProviderBase.ClusterCatalog.ContainsKey(objCacheJob.Key)) Then
                If objCacheJob.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "Start Get DistributedCaching", Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Key, False)
                End If

                ' getting the object from the distributed caching engine 
                Dim value As WrappedCacheObject = DistributedCachingProviderBase.ClientController.[Get](objCacheJob)

                If value IsNot Nothing Then

                    toReturn = LocalExtract(value, objCacheJob)
                    If Not objCacheJob.Strategy.NoLocalInsertOnDistributedCachingGet Then
                        SyncLock _CurrentInserts
                            _CurrentInserts(objCacheJob.Key) = toReturn
                        End SyncLock
                        Me.AsyncLocalInsertTaskQueue.EnqueueTask(New KeyValuePair(Of String, WrappedCacheObject)(objCacheJob.Key, value))
                    End If
                Else
                    If Not objCacheJob.Strategy.SkipCatalogCheck Then
                        SyncLock _LockClusterCatalog
                            ClusterCatalog.Remove(objCacheJob.Key)
                        End SyncLock
                    End If
                End If

                If objCacheJob.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "End Get DistributedCaching", Diagnostics.WorkingPhase.InProgress, True, objCacheJob.Key, toReturn IsNot Nothing)
                End If
            End If
            If objCacheJob.Strategy.BatchMode <> BatchMode.NoBatch Then
                If Not _CurrentAsyncBatchJobs.ContainsKey(objCacheJob.Strategy.GetTags) Then
                    SyncLock _CurrentAsyncBatchJobs
                        _CurrentAsyncBatchJobs(objCacheJob.Strategy.GetTags) = True
                    End SyncLock
                    AsynchronousBatchGetTaskQueue.EnqueueTask(objCacheJob)
                End If
            End If

            'Catch ex As Exception
            '    ProcessException(ex)
            'End Try

            Return toReturn

        End Function



        Private Sub PutInDistributedCaching(ByRef objCacheJob As CacheJob, ByVal objWrapped As WrappedCacheObject)

            Dim objSettings As DistributedCachingProviderSettings = DistributedCachingProviderBase.Settings

            If objSettings.CloudSettings.EnableDistributedCache Then

                Me.ResolveDistributionType(objCacheJob, objWrapped, objSettings)

                Select Case objCacheJob.Strategy.DistributionType

                    Case CacheDistributionType.XmlSerialized, CacheDistributionType.JsonNet, CacheDistributionType.Serialized, CacheDistributionType.NativeBinary, CacheDistributionType.AsString

                        Dim itemInfo As New WrappedDistributedCacheObject(objCacheJob, objWrapped)

                        AsynchronousDistributedCachingInsertTaskQueue.EnqueueTask(objSettings.CloudSettings.GetPutJob(objCacheJob, itemInfo))


                End Select

            End If

        End Sub


        Private Sub AsynchronousDistributedCachingInsert(ByVal objCacheJob As CachePutJob)
            Try
                If objCacheJob.Job.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingInsert, "DistributedCaching Insert Start", _
                                                                   Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Job.Key, False)
                End If
                If Not DistributedCachingProviderBase.Settings.CloudSettings.NoCatalog Then
                    SyncLock _LockClusterCatalog
                        ClusterCatalog.Item(objCacheJob.Job.Key) = True
                    End SyncLock
                    If objCacheJob.Job.Key <> Settings.CloudSettings.CatalogKey Then
                        _UpdateCatalog = True
                    End If
                End If
                ClientController.Put(objCacheJob)
                If objCacheJob.Job.Strategy.BatchMode = BatchMode.Bundle Then
                    Me.RemoveFromDistributedCaching(Settings.CloudSettings.GetBundleJob(objCacheJob.Job))
                End If
                If objCacheJob.Job.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingInsert, "DistributedCaching Insert End", _
                                                                   Diagnostics.WorkingPhase.InProgress, True, objCacheJob.Job.Key, True)
                End If
            Catch ex As Exception
                ProcessException(ex)
            End Try

        End Sub


        Private Sub RemoveFromDistributedCaching(ByVal objCacheJob As CacheJob)
            If Not objCacheJob.Strategy.DisableClusterRemoval AndAlso _
                ((objCacheJob.Strategy.DistributionType = CacheDistributionType.NotDistributed AndAlso objCacheJob.Strategy.NotifyRemovals) _
                OrElse (objCacheJob.Strategy.DistributionType <> CacheDistributionType.NotDistributed AndAlso ClientController.[Get](objCacheJob, True) IsNot Nothing)) Then
                ClientController.Remove(objCacheJob)
            End If
            If objCacheJob.ShouldSynchronize Then
                Dim depJob As CacheJob = Settings.CloudSettings.GetDependencyJob(objCacheJob)
                RemoveFromDistributedCaching(depJob)
            End If

        End Sub

        ''' <summary>
        ''' Extract actual object from his container
        ''' </summary>
        ''' <param name="cacheObject"></param>
        ''' <param name="objCacheJob"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Function LocalExtract(ByVal cacheObject As WrappedCacheObject, ByVal objCacheJob As CacheJob) As Object

            If TypeOf cacheObject Is WrappedDistributedCacheObject Then
                Dim objWrappedCache As WrappedDistributedCacheObject = DirectCast(cacheObject, WrappedDistributedCacheObject)

                ' logging Start Deserialize
                Dim compressionRatio As Double
                If objCacheJob.ShouldLog AndAlso objWrappedCache.DistributionType <> CacheDistributionType.Serialized Then
                    compressionRatio = objWrappedCache.SerializedValue.Length
                    Dim sizePair As New KeyValuePair(Of String, String)(CacheStepProperty.ObjectSize, compressionRatio.ToString)
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "Start Deserialize", _
                                                                                Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Key, False, sizePair)
                End If

                ' Extracting
                Dim toReturn As Object = objWrappedCache.Value

                ' logging End Deserialize
                If objCacheJob.ShouldLog AndAlso objWrappedCache.DistributionType <> CacheDistributionType.Serialized Then
                    Dim sizePair As New KeyValuePair(Of String, String)(CacheStepProperty.ObjectSize, objWrappedCache.SerializedValue.Length.ToString)
                    compressionRatio = Math.Abs(compressionRatio * 100 / objWrappedCache.SerializedValue.Length)
                    Dim logPair As New KeyValuePair(Of String, String)(CacheStepProperty.CompressionRatio, compressionRatio.ToString)
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "End Deserialize", _
                                                                            Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Key, True, sizePair, logPair)
                End If
                Return toReturn
            Else
                Return cacheObject
            End If

        End Function



        Friend Shared ReadOnly Property ClusterCatalog() As SerializableDictionary(Of String, Boolean)
            Get
                If _ClusterCatalog Is Nothing Then
                    If _RetrievingCatalog Then
                        Return _EmptyCatalog
                    Else
                        _RetrievingCatalog = True
                        _ClusterCatalog = DirectCast(DistributedCachingProviderBase.Instance.GetItem(DistributedCachingProviderBase.Settings.CloudSettings.CatalogKey), SerializableDictionary(Of String, Boolean))
                        If _ClusterCatalog Is Nothing Then
                            _ClusterCatalog = New SerializableDictionary(Of String, Boolean)
                            DistributedCachingProviderBase.Instance.Insert(DistributedCachingProviderBase.Settings.CloudSettings.CatalogKey, _ClusterCatalog, _
                                    Nothing, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.High, New CacheItemRemovedCallback(AddressOf CatalogRemovedHandler), False)
                        End If
                        _RetrievingCatalog = False
                    End If
                End If

                Return _ClusterCatalog
            End Get
        End Property

        Private Shared Sub CatalogRemovedHandler(ByVal key As String, ByVal value As Object, ByVal reason As CacheItemRemovedReason)
            _ClusterCatalog = Nothing
        End Sub


        Private _LockClusterCatalog As New Object




        Private Shared Sub AsynchronousGet(ByVal key As String)
            DistributedCachingProviderBase.Instance.GetItem(key)
        End Sub

        Private Sub AsynchronousBatchGet(ByVal objCacheJob As CacheJob)
            Try
                If objCacheJob.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "Start Batch Get", _
                                                                   Diagnostics.WorkingPhase.InProgress, False, objCacheJob.Strategy.GetTags, False)
                End If
                Dim tagPairs As IEnumerable(Of KeyValuePair(Of String, WrappedCacheObject))
                Select Case objCacheJob.Strategy.BatchMode
                    Case BatchMode.Bundle
                        Dim bundleJob As CacheJob = Settings.CloudSettings.GetBundleJob(objCacheJob)
                        tagPairs = DirectCast(Me.GetFromDistributedCaching(bundleJob), IEnumerable(Of KeyValuePair(Of String, WrappedCacheObject)))
                        If tagPairs Is Nothing Then
                            tagPairs = DistributedCachingProviderBase.ClientController.GetWithTags(objCacheJob)
                            If tagPairs IsNot Nothing Then
                                Dim bundleObject As New List(Of KeyValuePair(Of String, WrappedCacheObject))(tagPairs)
                                tagPairs = bundleObject

                                Dim wrappedObj As New WrappedCacheObject(bundleObject, False)
                                Me.PutInDistributedCaching(bundleJob, wrappedObj)
                            End If
                        End If
                    Case BatchMode.AsyncGetByTag
                        tagPairs = DistributedCachingProviderBase.ClientController.GetWithTags(objCacheJob)
                    Case Else
                        Exit Sub
                End Select
                If tagPairs IsNot Nothing Then
                    For Each objPair As KeyValuePair(Of String, WrappedCacheObject) In tagPairs
                        Dim keyJob As CacheJob = DistributedCachingProviderBase.Settings.CloudSettings.GetKeyJob(objPair.Key)
                        If keyJob.ShouldLog Then
                            DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "Start Get DistributedCaching", Diagnostics.WorkingPhase.InProgress, False, keyJob.Key, False)
                        End If
                        Dim extractedObject As Object = Me.LocalExtract(objPair.Value, keyJob)
                        SyncLock _CurrentInserts
                            _CurrentInserts(objPair.Key) = extractedObject
                        End SyncLock
                        Me.AsyncLocalInsertTaskQueue.EnqueueTask(objPair)
                        If keyJob.ShouldLog Then
                            DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "End Get DistributedCaching", Diagnostics.WorkingPhase.InProgress, True, keyJob.Key, True)
                        End If
                    Next
                End If

                If objCacheJob.ShouldLog Then
                    DistributedCachingBusinessController.LogCachingOperation(CacheTimings.DistributedCachingGet, "End Batch Get", _
                                                                   Diagnostics.WorkingPhase.InProgress, True, objCacheJob.Strategy.GetTags, True)
                End If
            Catch ex As Exception
                ProcessException(ex)
            Finally
                SyncLock _CurrentAsyncBatchJobs
                    If _CurrentAsyncBatchJobs.ContainsKey(objCacheJob.Strategy.GetTags) Then
                        _CurrentAsyncBatchJobs.Remove(objCacheJob.Strategy.GetTags)
                    End If
                End SyncLock
            End Try

        End Sub



        Friend Sub DistributorTaskQueue_ActionPerformed(ByVal sender As Object, ByVal e As GenericEventArgs(Of Integer))

            If _UpdateCatalog Then
                'no more items to add?
                If e.Item = 0 Then
                    _UpdateCatalog = False
                    Dim toInsert As New SerializableDictionary(Of String, Boolean)(ClusterCatalog)
                    Me.Insert(DistributedCachingProviderBase.Settings.CloudSettings.CatalogKey, toInsert, False)
                End If
            End If
        End Sub





        Private Sub ResolveDistributionType(ByRef objCacheJob As CacheJob, ByVal objWrapped As WrappedCacheObject, ByVal objSettings As DistributedCachingProviderSettings)
            If objCacheJob.Strategy.DistributionType = CacheDistributionType.Undefined Then

                If objWrapped.PersistAppRestart AndAlso DataCache.CachePersistenceEnabled Then
                    objCacheJob.Strategy.DistributionType = CacheDistributionType.XmlSerialized
                ElseIf Not objSettings.CloudSettings.SkipIConvertible AndAlso TypeOf objWrapped.Value Is IConvertible Then
                    objCacheJob.Strategy.DistributionType = CacheDistributionType.AsString
                ElseIf Aricie.Services.ReflectionHelper.IsFullySerializable(objWrapped.Value) Then
                    If objSettings.CloudSettings.DefaultSerializationMode <> CacheDistributionType.Serialized _
                            OrElse Not Aricie.Services.ReflectionHelper.ContainsGenericsSelfReference(objWrapped.Value) Then
                        objCacheJob.Strategy.DistributionType = objSettings.CloudSettings.DefaultSerializationMode
                    End If
                ElseIf objSettings.CloudSettings.EnforceXmlSerialization Then
                    Dim isXmlSerializable As Boolean = Nothing
                    If Not objSettings.CloudSettings.XmlSerializableTests.TryGetValue(objCacheJob.Key, isXmlSerializable) Then
                        Try
                            ReflectionHelper.Serialize(objWrapped.Value)
                            isXmlSerializable = True

                        Catch
                            isXmlSerializable = False
                        End Try
                        objSettings.CloudSettings.XmlSerializableTests(objCacheJob.Key) = isXmlSerializable

                    End If
                    If isXmlSerializable Then
                        objCacheJob.Strategy.DistributionType = CacheDistributionType.XmlSerialized
                    End If

                End If
                If objCacheJob.Strategy.DistributionType = CacheDistributionType.Undefined Then
                    objCacheJob.Strategy.DistributionType = CacheDistributionType.NotDistributed
                End If
            End If
        End Sub


        Private Shared Sub ProcessException(ByVal ex As Exception)
            Aricie.DNN.Diagnostics.AsyncLogger.Instance.AddException(ex)
            ClientController.StopTemporary()


        End Sub



#End Region


    End Class

End Namespace


